package com.xaicode.auth.security;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.xaicode.auth.security.domain.AuthRoleVo;
import com.xaicode.auth.security.domain.LoginUser;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 用户认证信息简易处理接口
 * 
 * @date 2024/12/6
 */
@Slf4j
public class LoginUtil {

    /**
     * 获取当前登录用户ID
     */
    public static String getLoginId() {
        return StpUtil.getLoginIdAsString();
    }

    /**
     * 当前登录用户是否为管理员
     */
    public static boolean isAdminLogin() {
        if (!StpUtil.isLogin()) {
            return false;
        }
        // 0 普通用户
        // 1 超级管理员
        return getLoginUser().getAdmin() == 1;
    }

    /**
     * 获取当前登录用户
     */
    public static LoginUser getLoginUser() {
        return getLoginUserBySaSession(StpUtil.getSession());
    }

    /**
     * 从SaSession中提取登录用户数据
     *
     * @param session SaSession
     * @return AuthUser
     */
    public static LoginUser getLoginUserBySaSession(SaSession session) {
        Object userStr = session.get(AuthConst.USER_KEY);
        Objects.requireNonNull(userStr);
        return JSONObject.parseObject(userStr.toString(), LoginUser.class);
    }

    public static String getNickname() {
        return Optional.ofNullable(getLoginUser())
                .map(LoginUser::getNickname)
                .orElse(null);
    }

    /**
     * 设置当前登录用户
     */
    public static void setLoginUser(LoginUser authUser) {
        StpUtil.getSession().set(AuthConst.USER_KEY, authUser);
    }

    /**
     * 设置当前登录用户拥有的角色
     */
    public static void setLoginUserRoles(List<AuthRoleVo> roles) {
        if (CollUtil.isEmpty(roles)) {
            return;
        }
        StpUtil.getSession().set(AuthConst.ROLE_KEY, roles);
    }

    /**
     * 获取当前用户的角色信息
     */
    public static List<AuthRoleVo> getLoginUserRoles() {
        if (!StpUtil.isLogin()) {
            return Lists.newArrayList();
        }
        Object roles = StpUtil.getSession().get(AuthConst.ROLE_KEY);
        List<AuthRoleVo> roleList = new ArrayList<>();
        try {
            roleList = JSONObject.parseArray(roles.toString(), AuthRoleVo.class);
        } catch (Exception e) {
            log.error("[SaToken] 获取用户角色信息失败：{}", e.getMessage(), e);
        }
        return roleList;
    }

    /**
     * 设置当前登录用户拥有的菜单权限
     */
    public static void setLoginUserPermissions(List<String> permissions) {
        if (CollUtil.isEmpty(permissions)) {
            return;
        }
        StpUtil.getSession().set(AuthConst.PERMISSION_KEY, permissions);
    }

    /**
     * 获取当前用户的权限信息
     */
    public static List<String> getLoginUserPermissions() {
        if (!StpUtil.isLogin()) {
            return Lists.newArrayList();
        }
        Object permissions = StpUtil.getSession().get(AuthConst.PERMISSION_KEY);
        List<String> permissionList = new ArrayList<>();
        try {
            permissionList = JSONObject.parseArray(permissions.toString(), String.class);
        } catch (Exception e) {
            log.error("[SaToken] 获取用户权限信息失败：{}", e.getMessage(), e);
        }
        return permissionList;
    }

    /**
     * 设置当前登录用户拥有的数据权限
     */
    public static void setLoginUserDataScopes(List<String> dataScopes) {
        if (CollUtil.isEmpty(dataScopes)) {
            return;
        }
        StpUtil.getSession().set(AuthConst.DATA_SCOPE_KEY, dataScopes);
    }

    /**
     * 获取当前用户的数据权限信息
     */
    public static List<String> getLoginUserDataScopes() {
        if (!StpUtil.isLogin()) {
            return Lists.newArrayList();
        }
        Object dataScopes = StpUtil.getSession().get(AuthConst.DATA_SCOPE_KEY);
        List<String> dataScopeList = new ArrayList<>();
        try {
            dataScopeList = JSONObject.parseArray(dataScopes.toString(), String.class);
        } catch (Exception e) {
            log.error("[SaToken] 获取用户数据权限信息失败：{}", e.getMessage(), e);
        }
        return dataScopeList;
    }

    /**
     * 构建当前登录用户的追加数据权限 SQL
     */
    public static String buildLoginUserDataScopeExSql(String tableAlias) {
        List<String> scopes = getLoginUserDataScopes();

        // !!! 默认 data_scope，匹配 BaseDataScopeEntity 字段
        String scopeField = "data_scope";

        // 构建查询字段
        String exSqlPre;
        if (StrUtil.isBlank(tableAlias)) {
            exSqlPre = scopeField;
        } else {
            exSqlPre = String.format("%s.%s", tableAlias, scopeField);
        }

        // 无数据权限时，仅能查询对应数据表的 dataScope 字段为空的数据
        if (scopes.isEmpty()) {
            // eg: (a.data_scope is null OR a.data_scope = '')
            return String.format(" (%s is null OR %s = '') ", exSqlPre, exSqlPre);
        }

        // 数据权限包含 * 时，可访问全部数据
        if (CollUtil.contains(scopes, AuthConst.DATA_SCOPE_FLAG_ALL)) {
            return "";
        }

        // 数据权限包含 self 时，表示该用户包含一个仅查询自身数据的判定
        // eg: create_by='userId' or u.create_by='userId'
        String exSqlSelf = "";
        if (CollUtil.contains(scopes, AuthConst.DATA_SCOPE_FLAG_SELF)) {
            String userId = getLoginId();
            if (StrUtil.isBlank(tableAlias)) {
                exSqlSelf = String.format(" create_by = '%s' ", userId);
            } else {
                exSqlSelf = String.format(" %s.create_by = '%s' ", tableAlias, userId);
            }
            CollUtil.removeAny(scopes, AuthConst.DATA_SCOPE_FLAG_SELF);
        }

        // 无更多数据权限时，返回个人数据权限过滤 sql
        if (scopes.isEmpty()) {
            return exSqlSelf;
        }

        // 仍有更多数据权限，将个人数据权限过滤 sql 作为参与条件
        if (StrUtil.isNotBlank(exSqlSelf)) {
            exSqlSelf = " OR " + exSqlSelf;
        }

        // 创建拼接 sql
        // eg: (a.data_scope IN ('1','2','3')
        //          OR a.data_scope is null
        //          OR a.data_scope = ''
        //          OR a.create_by = 'userId')
        // 转换数据权限标识格式
        scopes = scopes.stream()
                .map(scope -> String.format("'%s'", scope))
                .collect(Collectors.toList());
        return String.format(" (%s IN (%s) OR %s is null OR %s = '' %s) ",
                exSqlPre, String.join(",", scopes), exSqlPre, exSqlPre, exSqlSelf);
    }

}